/*
This software is distributed under the Simplified BSD License:

Copyright (c) 2008, Chris Venter <chris.venter@gmail.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, 
are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, 
    	this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, 
    	this list of conditions and the following disclaimer in the documentation 
    	and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
DAMAGE.
*/
#ifndef GENERIK_GTK_EVENTHANDLING_H
#define GENERIK_GTK_EVENTHANDLING_H

/** \file eventhandling.h
This file declares some wrappers for gtk event handlers.
*/

#include <map>
#include <gtk/gtk.h>

#include "../tracing.h"

namespace generik
{
	namespace gtk
	{
	
		struct dispatcher;
		
		struct realizer
		{
			typedef realizer THIS;
			
			realizer()
			{}
			realizer(GtkWidget* widget)
			{
				connect(widget);
			}
			
			void connect(GtkWidget* widget)
			{
				g_signal_connect_after(G_OBJECT(widget), "realize", G_CALLBACK(processrealize_static), this);
			}
			
			virtual void processrealize() = 0;
			static void processrealize_static(GtkWidget *widget, void* data)
			{
				((THIS*)(data))->processrealize();
			}
		};
		
		struct handler
		{
			typedef handler THIS;
			std::string name;
			
			virtual void activate()   {}
			virtual void deactivate() {}
			
			void connect_to(GtkWidget* widget_in, const std::string& signalname = "event")
			{
				g_signal_connect(G_OBJECT(widget_in), signalname.c_str(), G_CALLBACK(process_static), this);
			}
			
			/**
			\note Remember that if you have a class which multiply inherits from this class (handler)
			*and* some other classes, you may see some odd behaviour.
			This is because in this dispatching function, we statically cast the incoming class to
			a handler (because GTK is a C library and uses void*'s to pass 'unknown' instances of data around).
			This means that if the actual instance passed to us has consists more than just a handler base,
			the other virtual functions it has *might* get called.
			So make sure you cast your derived class to a handler *before* sending it down here!
			There is no way to dynamically check wether you have done this after the fact!
			\begincode
			struct A
			{};
			...
			struct C
			: A, handler, (etc...)
			{
				...
				void connect()
				{
					g_signal_connect(G_OBJECT(get_widget()), "event", G_CALLBACK(process_static), (handler*)this);
					//                                                                            ^^^^^^^^^^
				}
			};
			\endcode
			One other alternative remains, and that is to re-declare the static process_static function
			in the derived class. This is perhaps the better and more clear solution.
			*/
			static bool process_static(GtkWidget* widget_in, GdkEvent* event_in, void* data_in)
			{
				//std::cout<<(void*)h<<"(handler)::process_static"<<std::endl;
				return ((THIS*)data_in)->process(event_in);
			}
			
			virtual bool process(GdkEvent* event_in)
			{
				//GENERIK_TRACER;
				switch (event_in->type)
				{
					case GDK_DELETE:
						//GENERIK_INLINE(name<<": process delete");
						return processdelete(*event_in);
						
					case GDK_KEY_PRESS:
						//GENERIK_INLINE(name<<": process keydown");
						return processkeydown(event_in->key);
						
					case GDK_KEY_RELEASE:
						//GENERIK_INLINE(name<<": process keyup");
						return processkeyup(event_in->key);
						
					case GDK_EXPOSE:
						//GENERIK_INLINE(name<<": process expose");
						return processexpose(event_in->expose);
						
					case GDK_CONFIGURE:
						//GENERIK_INLINE(name<<": process configure");
						return processconfigure(event_in->configure);
						
					case GDK_MOTION_NOTIFY:
						//GENERIK_TRACE("process motion");
						return processmotion(event_in->motion);
						
					case GDK_BUTTON_PRESS:
					case GDK_2BUTTON_PRESS:
					case GDK_3BUTTON_PRESS:
						//GENERIK_INLINE(name<<": process buttondown");
						return processbuttondown(event_in->button);
						
					case GDK_BUTTON_RELEASE:
						//GENERIK_INLINE(name<<": process buttonup");
						return processbuttonup(event_in->button);
						
					default:
						//GENERIK_INLINE(name<<": process "<<event_in->type);
						return false;
				}
				return false;
			}
			
#define RETFALSE { return false; }

			virtual bool processdelete		(GdkEvent& event) 		RETFALSE
			virtual bool processkeydown		(GdkEventKey& event) 		RETFALSE
			virtual bool processkeyup		(GdkEventKey& event) 		RETFALSE
			virtual bool processexpose		(GdkEventExpose& event_in) 	RETFALSE
			virtual bool processconfigure		(GdkEventConfigure& event_in) 	RETFALSE
			virtual bool processmotion		(GdkEventMotion& event_in) 	RETFALSE
			virtual bool processbuttondown		(GdkEventButton& event_in) 	RETFALSE
			virtual bool processbuttonup		(GdkEventButton& event_in) 	RETFALSE
		};
		
		struct dispatcher
		: handler
		{
			typedef dispatcher THIS;
			typedef handler HANDLER;
			
			dispatcher()
			{
				name = "dispatcher";
			}
			
			static bool process_static(GtkWidget* widget_in, GdkEvent* event_in, void* data_in)
			{
				return ((THIS*)data_in)->process(event_in);
			}
			
			std::list<HANDLER*> handlers;
			std::map<std::string, HANDLER*> registered_handlers;
			
			bool process(GdkEvent* event_in)
			{
				REM_GENERIK_TRACER;
				bool handled = false;
				for (std::list<handler*>::iterator i = handlers.begin(); i != handlers.end(); ++i)
				{
					handled = (*i)->process(event_in);
					if (handled) break;
				}
				return handled;
			}

			/** Activates the specified handler and puts it in front.
			*/
			virtual void activate_handler(HANDLER* h)
			{
				h->activate();
				handlers.push_front(h);
			}
			/** Activates the handler by name, if registered.
			*/
			virtual void activate_handler(const std::string& name)
			{
				std::map<std::string, HANDLER*>::iterator found = registered_handlers.find(name);
				if (found != registered_handlers.end())
				{
					activate_handler(found->second);
				}
			}
			/** Deactivates the specified handler (and implicitly, all handlers ahead of it).
			*/
			//virtual void deactivate_handler(HANDLER* h)
			//{}
			/** Deactivates the handler by name (and all handlers ahead of it).
			*/
			//virtual void deactivate_handler(const std::string& name)
			//{}
			/** Deactivates the front-most handler.
			*/
			virtual void deactivate_handler()
			{
				if (!handlers.empty())
				{
					handlers.front()->deactivate();
					handlers.pop_front();
				}
			}
			/*
			bool processdelete		(GdkEvent& event) 		{ return false; }
			bool processkeydown		(GdkEventKey& event) 		{ return false; }
			bool processkeyup		(GdkEventKey& event) 		{ return false; }
			bool processexpose		(GdkEventExpose& event_in) 	{ return false; }
			bool processconfigure		(GdkEventConfigure& event_in) 	{ return false; }
			bool processmotion		(GdkEventMotion& event_in) 	{ return false; }
			bool processbuttondown		(GdkEventButton& event_in) 	{ return false; }
			bool processbuttonup		(GdkEventButton& event_in) 	{ return false; }
			*/
		};
		
	}
}

#endif








